#!/usr/bin/perl
#
# Migrate the CUPS configuration, including print queues, from SRCROOT to
# DSTROOT.  Does *not* migrate AppleTalk (pap) queues since AppleTalk is gone.
#

#### Common stuff ####
my $SRCROOT = $ENV{SRCROOT};
my $DSTROOT = $ENV{DSTROOT};

#### cupsd.conf migration ####
# Collect configuration options from the old cupsd.conf file...
my $browsing = 1;
my $loglevel = "warn";
my $protocols = "CUPS dnssd";
my $sharing = 0;
my $remoteAdmin = 0;
my $inAdmin = 0;
my @extralines;

open(CUPSCONF, $SRCROOT . "/private/etc/cups/cupsd.conf");

while (<CUPSCONF>)
{
	if ( m/^Port 631/ )
	{
		$sharing = 1;
	}
	elsif ( m/^LogLevel\s*debug/ )
	{
		s/^LogLevel\s*//;
		s/\s*$//;
		$loglevel = $_;
	}
	elsif ( m/^Browsing\s*Off/ )
	{
		$browsing = 0;
	}
	elsif ( m/^BrowseLocalProtocols\s/ )
	{
		s/^BrowseLocalProtocols\s*//;
		s/\s*$//;
		s/@CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS@//;
		if ( $_ )
		{
			$protocols = $_;
		}
	}
	elsif ( m/^<Location \/admin>/ )
	{
		$inAdmin = 1;
	}
	elsif ( m/^<\/Location>/ )
	{
		$inAdmin = 0;
	}
	elsif ( ( m/Allow @LOCAL/ || m/Allow From @LOCAL/ || m/Allow all/ || m/Allow From all/ ) && $inAdmin != 0)
	{
		$remoteAdmin = 1;
	}
	elsif ( m/^(BrowseAddress|BrowseRemoteProtocols|MaxClients|MaxJobs|MaxLogSize|PreserveJobFiles)\s/ )
	{
		push(@extralines, $_);
	}
}

close(CUPSCONF);

# Read the new cupsd.conf file and rewrite it with the correct options...
if ($browsing != 1 || $loglevel != "warn" || $protocols != "" || $sharing != 0 || $remoteAdmin != 0 || @extralines > 0)
{
	my @lines;
	open(CUPSCONF, $DSTROOT . "/private/etc/cups/cupsd.conf");
	while(<CUPSCONF>)
	{
		if ( $browsing != 1)
		{
			s/^Browsing On/Browsing Off/;
		}

		if ( $sharing == 1)
		{
			s/^# Only listen for connections from the local machine./# Allow remote access/;
			s/^Listen localhost:631/Port 631/;
			s/^# Show shared printers on the local network./# Enable printer sharing and shared printers./;
		}

		if ( m/^LogLevel\s/ )
		{
			push(@lines, "LogLevel $loglevel\n");
		}
		elsif ( m/^BrowseLocalProtocols\s/ && $protocols != "")
		{
			push(@lines, "BrowseLocalProtocols $protocols\n");
		}
		elsif ( m/^<Location \/>/ && $sharing != 0 )
		{
			push(@lines, $_);
			push(@lines, "  Allow \@LOCAL\n");
		}
		elsif ( m/^<Location \/admin/ && $remoteAdmin != 0 )
		{
			push(@lines, $_);
			push(@lines, "  Allow \@LOCAL\n");
		}
		else
		{
			push(@lines, $_);
		}
	}
	close(CUPSCONF);
	open(CUPSCONF, ">" . $DSTROOT . "/private/etc/cups/cupsd.conf");
	print CUPSCONF "# Migrated cupsd.conf\n";
	foreach ( @lines )
	{
		print CUPSCONF $_;
	}
	foreach ( @extralines )
	{
		print CUPSCONF $_;
	}
	close(CUPSCONF);
}


#### printers.conf migration ####
# Copy printers.conf and PPDs, filtering out AppleTalk printers...
my %printers;
my $printer;
my @plines;
my $legacy;
my $defaultp;
my $onnet = 0;

#	Find out whether the source OS is a server install
system("${DSTROOT}/System/Library/PrivateFrameworks/ServerInformation.framework/Resources/serverinfo", "--software", "${SRCROOT}");
my $notServerOS = ($? >> 8);

if ( open(OLDPRINTERS, $SRCROOT . "/private/etc/cups/printers.conf") )
{
	open(NEWPRINTERS, ">" . $DSTROOT . "/private/etc/cups/printers.conf");

	print NEWPRINTERS "# Migrated printers.conf\n";

	$printer  = "";
	@plines   = ();
	$legacy   = 0;
	$defaultp = 0;

	while ( <OLDPRINTERS> )
	{
		if ( m/^<Printer\s/ )
		{
			# Start of a printer definition
			s/^<Printer\s*//;
			s/>\s*$//;
			$printer  = $_;
			$defaultp = 0;
			$onnet    = 0;
		}
		elsif ( m/^<DefaultPrinter\s/ )
		{
			# Start of a default printer definition
			s/^<DefaultPrinter\s*//;
			s/>\s*$//;
			$printer  = $_;
			$defaultp = 1;
			$onnet    = 0;
		}
		elsif ( m/^<\/Printer>/ )
		{
			# End of a printer definition
			if ( $printer && !$legacy )
			{
				# Copy printer definition to new printers.conf
				if ( $defaultp )
				{
					print NEWPRINTERS "<DefaultPrinter " . $printer . ">\n";
				}
				else
				{
					print NEWPRINTERS "<Printer " . $printer . ">\n";
				}

				foreach (@plines)
				{
					print NEWPRINTERS $_;
				}
				print NEWPRINTERS "</Printer>\n";

			    if ( open(OLDPPD, $SRCROOT . "/private/etc/cups/ppd/" . $printer . ".ppd") )
				{
					# Copy the PPD file for this printer
					open(NEWPPD, ">" . $DSTROOT . "/private/etc/cups/ppd/" . $printer . ".ppd");
					while ( <OLDPPD> )
					{
						print NEWPPD $_;
					}

					close(OLDPPD);
					close(NEWPPD);
				}

				# Add the printer to the hash of printers
				$printers{$printer} = $printer;
			}

			$printer = "";
			@plines  = ();
			$legacy  = 0;
			$onnet   = 0;
		}
		elsif ( m/^DeviceURI\s+pap:/ )
		{
			# AppleTalk Device URI
			$legacy = 1;
		}
		elsif ( m;^DeviceURI\s+(file:(//)?)?/dev/null; )
		{
			# Tioga Device URI
			$legacy = 1;
		}
		elsif ( $printer )
		{
			#	In the middle of a printer definition block
			$onnet = 1 if ( m/^DeviceURI\s+(ipp|ipps|dnssd|lpd|socket|riousbprint|http|https|smb):/ );
			s/^Shared\s+Yes/Shared No/ if ($onnet && $notServerOS);
			push(@plines, $_);
		}
	}

	close(OLDPRINTERS);
	close(NEWPRINTERS);
}


#### classes.conf migration ####
# Copy classes.conf, filtering out printers that were not migrated...
if ( open(OLDCLASSES, $SRCROOT . "/private/etc/cups/classes.conf") )
{
	open(NEWCLASSES, ">" . $DSTROOT . "/private/etc/cups/classes.conf");
	print NEWCLASSES "# Migrated classes.conf\n";

	while ( <OLDCLASSES> )
	{
		if ( m/^Printer\s/ )
		{
			s/^Printer\s*//;
			s/\s*$//;
			$printer = $_;

			if ( exists $printers{$printer} )
			{
				print NEWCLASSES "Printer " . $printer . "\n";
			}
		}
		else
		{
			print NEWCLASSES $_;
		}
	}

	close(OLDCLASSES);
	close(NEWCLASSES);
}


#### /Library/Printers migration cleanup ####
# Make sure all files are owned by root and all directories and executables do
# not have group-write and world-write permissions...
sub FixPermissions
{
	my($directory) = @_;

	if ( opendir(DIR, $directory) )
	{
		my @filenames = readdir(DIR);
		closedir(DIR);

		foreach $filename (@filenames)
		{
			# Skip "." and ".."
			if ( $filename ne "." && $filename ne ".." )
			{
				# Get permissions...
				$path = $directory . "/" . $filename;

				($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat($path);

				# Only change files and directories with the execute bit set
				if ( $mode & 0111 )
				{
					# Check for setuid, group-write or world-write...
					if ( $mode & 04022 )
					{
						chmod($mode & 0755, $path);
					}

					# Check for root ownership...
					if ( $uid )
					{
						chown(0, $gid, $path);
					}

					# Fix subdirectories...
					if ( $mode & 040000 )
					{
						FixPermissions($path);
					}
				}
			}
		}
	}
}

FixPermissions($DSTROOT . "/Library/Printers");
FixPermissions($DSTROOT . "/usr/libexec/cups");

#### Icon permissions cleanup ####
# Make sure legacy AirPrint icon files are owned by root and are readable by all
my $directory = "/Library/Caches";
if ( opendir(DIR, $directory ) )
{
	my @filenames = readdir(DIR);
	closedir(DIR);
	
	foreach $filename (@filenames)
	{
		if ( $filename =~ m/^com\.apple\.ipp2ppd\..*\.icns/)
		{
			# Get permissions...
			$path = $directory . "/" . $filename;
			
			($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat($path);
			
			if ( ( $mode & 0777 ) != 0644 )
			{
				chmod(0644, $path);
			}
			
			# Check for root ownership...
			if ( $uid )
			{
				chown(0, $gid, $path);
			}
		}
	}
}
